﻿Imports Org.BouncyCastle.X509
Imports Org.BouncyCastle.Math
Imports Org.BouncyCastle.Security
Imports Org.BouncyCastle.Asn1.X509
Imports Org.BouncyCastle.Asn1
Imports Org.BouncyCastle.X509.Extension
Imports Org.BouncyCastle.Asn1.Pkcs
Imports SharedTools
Imports System.Security.Cryptography

Public Class CertificatesManager
    Public Shared Function CreateCertificateFromRequest(request As CertificateRequestItem) As X509Certificate
        Dim certGen As New X509V3CertificateGenerator()
        Dim pubKey = PublicKeyFactory.CreateKey(request.Request.GetCertificationRequestInfo.SubjectPublicKeyInfo)

        certGen.SetSerialNumber(BigInteger.ValueOf(request.ID))
        certGen.SetIssuerDN(Repository.Data.MyCertificate.SubjectDN)
        certGen.SetNotBefore(DateTime.Now)
        certGen.SetNotAfter(DateTime.Now.AddSeconds(My.Settings.CertsLife))
        certGen.SetSubjectDN(request.Request.GetCertificationRequestInfo.Subject)
        certGen.SetPublicKey(pubKey)
        certGen.SetSignatureAlgorithm("SHA512withRSA")

        certGen.AddExtension(X509Extensions.KeyUsage, True, New KeyUsage(If(request.RequestType = SharedTools.CertificateType.Signature, KeyUsage.DigitalSignature, KeyUsage.DataEncipherment)))

        Dim atts = request.Request.GetCertificationRequestInfo.Attributes
        For Each item In atts
            Dim attr = New Org.BouncyCastle.Asn1.Cms.Attribute(CType(item, Asn1Sequence))
            If attr.AttrType.Equals(PkcsObjectIdentifiers.Pkcs9AtExtendedCertificateAttributes) Then
                Dim extensions As X509Extensions = X509Extensions.GetInstance(attr.AttrValues(0))
                For Each e In extensions.ExtensionOids
                    Dim oid As DerObjectIdentifier = DirectCast(e, DerObjectIdentifier)
                    Dim ext As X509Extension = extensions.GetExtension(oid)
                    If oid.Equals(X509Extensions.SubjectAlternativeName) Then
                        certGen.AddExtension(X509Extensions.SubjectAlternativeName, ext.IsCritical, ext.Value)
                    End If
                Next
            End If
        Next

        Dim attributes As Asn1Set = request.Request.GetCertificationRequestInfo().Attributes

        Dim issuedCert As X509Certificate = certGen.Generate(Repository.Data.BouncyCastlePrivateKey)

        Return issuedCert
    End Function

    Public Shared Function GetCurrentCertificateByUsername(username As String, t As CertificateType) As CertificateItem
        Dim certs = (From c In Repository.Certificates Select c Where c.Username = username AndAlso c.CertificateType = t AndAlso c.IsCurrent)
        If certs.Count > 0 Then
            Return certs(0)
        Else
            Return Nothing
        End If
    End Function

    Public Shared Function GetCertificateByID(id As Integer) As CertificateItem
        Dim certs = (From c In Repository.Certificates Select c Where c.ID = id)
        If certs.Count > 0 Then
            Return certs(0)
        Else
            Return Nothing
        End If
    End Function

    Public Shared Sub ShowCertificateDetails(cert As X509Certificate)
        Dim c2 = New X509Certificates.X509Certificate2(DotNetUtilities.ToX509Certificate(cert))
        X509Certificates.X509Certificate2UI.DisplayCertificate(c2)
    End Sub

    Shared Sub RefreshCertificatesStatus(sender As Object, e As Timers.ElapsedEventArgs)
        For Each item In Repository.Certificates
            item.Refresh()
        Next
    End Sub

    Shared Function CertificateDownloadRequest(r As String) As String
        Dim senderUsername = XmlTool.GetValue("Username", r)
        Dim senderCert = GetCurrentCertificateByUsername(senderUsername, CertificateType.Signature)
        Dim resp = XmlTool.GenerateXML("CertificateDownloadResponse")
        If senderCert Is Nothing Then
            resp = XmlTool.AddAttribute(resp, "CertificateDownloadResponse", "Response", CertificateDownloadResponse.ClientUnknownException.ToString)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        End If

        If XmlTool.CheckSignature(r, DirectCast(senderCert.Certificate.GetPublicKey, Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)) Then
            Dim targetUsername = XmlTool.GetValue("TargetUsername", r)
            Dim targetCertificateType = DirectCast([Enum].Parse(GetType(CertificateType), XmlTool.GetValue("CertificateType", r)), CertificateType)

            Dim targetCert = GetCurrentCertificateByUsername(targetUsername, targetCertificateType)
            If targetCert Is Nothing Then
                resp = XmlTool.AddAttribute(resp, "CertificateDownloadResponse", "Response", CertificateDownloadResponse.TargetUserUnknownOrMissingCertificate.ToString)
            Else
                resp = XmlTool.AddAttribute(resp, "CertificateDownloadResponse", "Response", CertificateDownloadResponse.Ok.ToString)
                resp = XmlTool.AddElement(resp, "CertificateDownloadResponse", "Certificate", Convert.ToBase64String(targetCert.Certificate.GetEncoded()))
            End If
        Else
            resp = XmlTool.AddAttribute(resp, "CertificateDownloadResponse", "Response", CertificateDownloadResponse.SignatureException.ToString)
        End If

        Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
    End Function

End Class
